#ifndef FLASH_H
#define FLASH_H

#include "Arduino.h" // for byte data type
#include "SPI.h"

enum State 
{
  STATE_NONE, 
  STATE_PROGRAM_LOAD, 
  STATE_READ_FROM_CACHE, 
  STATE_PROGRAM_EXECUTE, 
  STATE_PAGE_READ,
  STATE_BLOCK_ERASE
};

enum OperationStatus
{
	OS_IN_PROGRESS = 0,
	OS_SUCCESS,
	OS_ERASE_FAIL,
	OS_WRITE_FAIL
};

enum FlashCommand 
{
  BLOCK_ERASE = 0xd8, 
  GET_FEATURE = 0x0f, 
  PAGE_READ = 0x13, 
  PROGRAM_EXECUTE = 0x10, 
  PROGRAM_LOAD = 0x02, 
  PROGRAM_LOAD_RANDOM_DATA = 0x84,
  READ_FROM_CACHE = 0x03,
  READ_FROM_CACHE_2 = 0x3b,
  READ_FROM_CACHE_4 = 0x6b,
  READ_ID = 0x9f,
  RESET = 0xff,
  SET_FEATURE = 0x1f,
  WRITE_DISABLE = 0x04,
  WRITE_ENABLE = 0x06,
  DUMMY_BYTE = 42
};

enum FeatureByte
{
  FEATURE_BLOCK_LOCK = 0xA0,
  FEATURE_OTP = 0xB0,
  FEATURE_STATUS = 0xC0
};

enum BlockLock
{
  LOCK_ALL = 0x07,
  LOCK_2 = 0x06,
  LOCK_4 = 0x05,
  LOCK_8 = 0x04,
  LOCK_16 = 0x03,
  LOCK_32 = 0x02,
  LOCK_64 = 0x01,
  LOCK_NONE = 0x00
};

class Flash
{	
  	public:
	    Flash(bool enable_write, bool restart_address_counter);
    	
	    void writeBytes(byte* bytes, int length);
		void cacheToArray();	
		void dumpArray(uint32_t startAddress, uint32_t endAddress, int amount);	
		void dumpArray(uint32_t startAddress, uint32_t endAddress, int amount, bool dumpAscii);
    
	    uint16_t readID();
		void restartAddressCounter();
    
	private:	
	  	
		bool write_enabled;
		bool dumps_ascii;
		
	    State state;
		
		uint32_t currentPage;
    	uint32_t currentByte;
		
		int slaveSelectPin;
		int writeProtectPin;
		int holdPin;
	
		void blockUntilFinished();
		
	    void writeEnable();
    
	    void programLoadStart(int plane, uint16_t column);
	    void programLoad(byte data);   
	    void programLoadEnd();  

	    void readFromCacheStart(int plane, uint16_t column);   
	    int readFromCache();
	    void readFromCacheEnd();

	    void pageRead(uint32_t address);
	    void programExecute(uint32_t address);  

		void blockErase(uint32_t address);

	    boolean changeState(State changeTo);

	    void setLocks(BlockLock data);
	
		void writeEeprom32(uint32_t data, uint32_t address);
		uint32_t readEeprom32(uint32_t address);
		void updateEepromPageAddress(uint32_t address);
		uint32_t getEepromPageAddress();
		void updateEepromByteAddress(uint32_t address);
		uint32_t getEepromByteAddress();
	
		int getPlane(uint32_t address);
		int getCurrentPlane();
	
	    void reset();   
	    OperationStatus  operationStatus();
	    int  getFeature(FeatureByte address);   
	
		void csh();
		void csl();
	};

#endif
